package com.stox.client.ui.javafx.chartingy.model.plot;

import java.util.ArrayList;
import java.util.List;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;

import com.stox.client.ui.core.model.intf.Configurable;
import com.stox.client.ui.javafx.chartingy.model.chart.Chart;
import com.stox.client.ui.javafx.chartingy.model.node.AreaPlotNode;
import com.stox.client.ui.javafx.chartingy.model.node.LinePlotNode;
import com.stox.client.ui.javafx.chartingy.model.node.StochasticPlotNode;
import com.stox.client.ui.javafx.chartingy.view.ChartingView;
import com.stox.client.ui.javafx.widget.ConfigurationPopupControl;
import com.stox.client.ui.javafx.widget.auto.BeanAutoUI;
import com.stox.client.ui.javafx.workbench.stage.StoxStage;
import com.stox.shared.core.beans.BeanDescriptor;
import com.stox.shared.indicator.model.enumeration.IndicatorDisplayType;
import com.stox.shared.indicator.model.enumeration.IndicatorPosition;
import com.stox.shared.indicator.model.intf.Indicator;
import com.stox.shared.indicator.model.intf.Value;

public class IndicatorPlot<OUT, CONFIG> extends Plot<Indicator<OUT, CONFIG>, OUT> implements Configurable {

	private final CONFIG configuration;
	private IndicatorPosition position;
	private IndicatorDisplayType displayType;

	public IndicatorPlot(final Indicator<OUT, CONFIG> indicator, ChartingView chartingView) {
		super(chartingView);
		setExtraValue(indicator);
		configuration = indicator.createDefaultConfiguration();
		displayType = indicator.getDisplayType(configuration);
		displayNameProperty().set(createDisplayName());
	}

	private String createDisplayName() {
		return getExtraValue().getDisplayName()+"("+configuration+")";
	}
	
	public CONFIG getConfiguration(){
		return configuration;
	}

	@Override
	public double getMax() {
		switch (position) {
			case INDEPENDENT:
				return isPrimary() ? super.getMax() : getChart().getPrimaryPlot().getMax();
			case OVERLAY_PRICE:
				return primaryPlot().getMax();
			case OVERLAY_VOLUME:
				return chartingView().getVolumePlot().getMax();
		}
		return 0;
	}

	@Override
	public double getMin() {
		switch (position) {
			case INDEPENDENT:
				return isPrimary() ? super.getMin() : getChart().getPrimaryPlot().getMin();
			case OVERLAY_PRICE:
				return primaryPlot().getMin();
			case OVERLAY_VOLUME:
				return chartingView().getVolumePlot().getMin();
		}
		return 0;
	}

	@Override
	public void configure() {
		final BeanDescriptor beanDescriptor = new BeanDescriptor(configuration);
		final BeanAutoUI autoUI = new BeanAutoUI(beanDescriptor);
		final ConfigurationPopupControl configurationPopupControl = new ConfigurationPopupControl(autoUI);
		configurationPopupControl.getApplyButton().setOnAction(new EventHandler<ActionEvent>() {
			@Override
			public void handle(final ActionEvent event) {
				autoUI.setValue(); // Values are transfered to beanDescriptor
				beanDescriptor.populateBean(configuration); // Values are transferred to configuration
				setPosition(getChart().getChartingView(), getExtraValue().getPosition(configuration));
				displayType = getExtraValue().getDisplayType(configuration);
				displayNameProperty().set(createDisplayName());
				load();
				getChart().updateAxisBounds();
			}
		});
		configurationPopupControl.show(StoxStage.INSTANCE);
	}

	@SuppressWarnings({"rawtypes", "unchecked"})
	public void setPosition(final ChartingView chartingView, final IndicatorPosition position) {
		this.position = position;

		if (null != getChart()) {
			getChart().getPlots().remove(this);
		}
		if ((null != getChart()) && equals(getChart().getPrimaryPlot())) {
			getChart().setPrimaryPlot(null);
		}
		Chart chart = null;
		switch (position) {
			case INDEPENDENT:
				chart = new Chart(chartingView);
				chart.setPrimaryPlot(this);
				chartingView.getCharts().add(chart);
				break;
			case OVERLAY_PRICE:
				chart = chartingView.getPrimaryChart();
				chart.getPlots().add(this);
				break;
			case OVERLAY_VOLUME:
				chart = chartingView.getVolumePlot().getChart();
				if (null != chart) {
					chart.getPlots().add(this);
				}
				break;
		}
	}

	@Override
	public void load() {
		final OUT[] result = getExtraValue().getValues(configuration, primaryPlot().getBars());
		final List<Data<Indicator<OUT, CONFIG>, OUT>> datas = new ArrayList<>(result.length);
		for (int index = 0; index < result.length; index++) {
			final OUT value = result[index];
			if (null != value) {
				Data<Indicator<OUT, CONFIG>, OUT> data = null;
				if (value instanceof Double) {
					final Double val = (Double) value;
					data = new Data<Indicator<OUT, CONFIG>, OUT>(0l, val, val, val, value, this, index);
				}else if(value instanceof Value){
					final Value val = (Value)value;
					data = new Data<Indicator<OUT, CONFIG>, OUT>(0l, val.realValue(), val.getMax(), val.getMin(), value, this, index);
				}
				datas.add(data);
			}
		}
		dataProperty().setAll(datas);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public void updateDataNodes(final List<? extends Data<Indicator<OUT, CONFIG>, OUT>> datas) {
		switch (displayType) {
			case AREA:
				for (final Data<Indicator<OUT, CONFIG>, OUT> data : datas) {
					data.setDataNode(null);
				}
				setPlotNode(new AreaPlotNode<>(this));
				break;
			case BAR:
				break;
			case BOUNDED_AREA:
				break;
			case LINE:
				for (final Data<Indicator<OUT, CONFIG>, OUT> data : datas) {
					data.setDataNode(null);
				}
				setPlotNode(new LinePlotNode<>(this));
				break;
			case DUAL_LINE : 
				for (final Data<Indicator<OUT, CONFIG>, OUT> data : datas) {
					data.setDataNode(null);
				}
				setPlotNode(new StochasticPlotNode(this));
				break;
			case MACD:
				break;
			default:
				break;
		}
	}

}
